﻿using System;
using System.Text;

using Evaluant.Uss.DomainModel.OptionModel;
using Models = Evaluant.Uss.Models;
using Evaluant.Uss.SqlMapper;

namespace MappingGenerator
{
    /// <summary>
    /// Build the mapping of the class collection
    /// which use the Table Per Hierarchy strategy
    /// </summary>
    public class TablePerHierarchy : IMappingImpl
    {
        public TablePerHierarchy(Models.Model model, Option option)
            : base(model, option)
        {
        }

        private System.Collections.Hashtable tableNames = new System.Collections.Hashtable();

        public override Mapping CreateMapping()
        {
            Mapping mapping = new Mapping();

            // Creates a table for each base type
            foreach (Models.Entity entity in _model.GetBaseEntities())
            {
                if (_model.GetEntity(entity.Type) == null)
                    continue;

                EntityOption parentEntityOption = GetEntityOption(entity.Type);

                if (!tableNames.Contains(parentEntityOption.TableName))
                    tableNames.Add(parentEntityOption.TableName, 0);
                else
                {
                    tableNames[parentEntityOption.TableName] = (int)tableNames[parentEntityOption.TableName] + 1;
                    parentEntityOption.TableName = string.Concat(parentEntityOption.TableName, (int)tableNames[parentEntityOption.TableName]);
                    
                }

                string parentDiscriminator;
                if (parentEntityOption.DiscriminatorField != String.Empty && parentEntityOption.DiscriminatorField != null)
                    parentDiscriminator = parentEntityOption.DiscriminatorField;
                else
                    parentDiscriminator = "Type";

                string parentTable = parentEntityOption.TableName;

                EntityMapping parentEntityMapping = BuildEntityMapping(entity, parentEntityOption);

                //	Adds a descriminator field in the current type has sub types
                if (_model.GetTree(entity.Type).Count > 1)
                {
                    //	If entity option don't define any discriminator
                    if ((parentEntityMapping.DiscriminatorField == null || parentEntityMapping.DiscriminatorField == string.Empty))
                    {
                        parentEntityMapping.DiscriminatorField = parentDiscriminator;
                        parentEntityMapping.DiscriminatorValue = entity.Type;
                    }
                }

                mapping.Entities.Add(parentEntityMapping);

                foreach (Models.Entity child in _model.GetTree(entity.Type))
                {
                    if (child == entity)
                        continue;

                    EntityOption childEntityOption = GetEntityOption(child.Type);

                    childEntityOption.DiscriminatorField = parentDiscriminator;
                    childEntityOption.TableName = parentTable;

                    EntityMapping childEntityMapping = BuildEntityMapping(child, childEntityOption);
                    
                    // Replaces all Ids by its parent's one as all hte hierarchy is inside the same table
                    childEntityMapping.Ids.Clear();
                    childEntityMapping.Ids.Add(parentEntityMapping.Ids[0]);

                    mapping.Entities.Add(childEntityMapping);
                }
            }

            // Loops over all relationships to create the corresponding mappings
            foreach (Models.Reference relation in _model.GetAllReferences())
            {
                EntityMapping parentEntity = mapping.Entities[relation.ParentType];
                EntityMapping childEntity = mapping.Entities[relation.ChildType];

                if (parentEntity == null || childEntity == null)
                    continue;

                //if (parentEntity == null)
                //    throw new ApplicationException(String.Format("The type [{0}] was not found for the relationship named [{1}]", relation.ParentType, relation.Name));

                //if (childEntity == null)
                //    throw new ApplicationException(String.Format("The type [{0}] was not found for the relationship named [{1}] in [{2}]", relation.ChildType, relation.Name, relation.ParentType));

                BuildRelationshipMapping(relation, mapping, parentEntity, childEntity);
            }

            return mapping;
        }

    }
}
